﻿using Microsoft.VisualStudio.TestTools.UnitTesting;
using Sustainalytics.ClientDW.DataAccessLayer.ESGRatings;
using Sustainalytics.Entities.ESGRatings;
using Sustainalytics.ESG.Entities;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Sustainalytics.ClientDW.DataAccessLayer.Tests
{
    [TestClass]
    public class WeightMatricesRepositoryTests
    {
        private const string ConnectionString = "mongodb://localhost:27017/ClientDW_Tests";
        private IWeightMatricesRepository _repository;

        [TestInitialize]
        public void Initialize()
        {
            _repository = WeightMatricesRepository.GetInstance(ConnectionString);
        }

        [TestCleanup]
        public void Cleanup()
        {
            _repository.Cleanup();
        }

        [TestMethod]
        public void GetWeightMatrix()
        {
            const string weightMatrixName = "Default Weight Matrix";

            var weightMatrix = new WeightMatrix
            {
                Id = Guid.NewGuid(),
                AccountId = null,
                Name = weightMatrixName,
                IndicatorsNumbers = new List<string> { "A.1.2", "B.2.1", "C.11.22" }
            };

            _repository.SaveWeightMatrix(weightMatrix);

            var dbWeightMatrix = _repository.GetWeightMatrixByNameAndAccountId(weightMatrixName.ToLowerInvariant());

            Assert.IsNotNull(dbWeightMatrix, string.Format("No weight matrix found for name '{0}'", weightMatrixName));
            Assert.IsTrue(dbWeightMatrix.Id == weightMatrix.Id, string.Format("The weight matrix found for name '{0}' has a wrong Id", weightMatrixName));
            Assert.IsTrue(dbWeightMatrix.AccountId == weightMatrix.AccountId, string.Format("The weight matrix found for name '{0}' has a wrong AccountId", weightMatrixName));
        }

        [TestMethod]
        public void SaveWeightMatrix()
        {
            const string weightMatrixName = "Default Weight Matrix";

            var weightMatrix = new WeightMatrix
            {
                Id = Guid.NewGuid(),
                AccountId = null,
                Name = weightMatrixName,
                IndicatorsNumbers = new List<string> { "A.1.2", "B.2.1", "C.11.22" }
            };

            _repository.SaveWeightMatrix(weightMatrix);

            var dbWeightMatrix = _repository.GetWeightMatrixByNameAndAccountId(weightMatrixName.ToLowerInvariant());

            Assert.IsNotNull(dbWeightMatrix, string.Format("No weight matrix found for name '{0}'", weightMatrixName));
            Assert.IsTrue(dbWeightMatrix.Id == weightMatrix.Id, string.Format("The weight matrix found for name '{0}' has a wrong Id", weightMatrixName));
            Assert.IsTrue(dbWeightMatrix.AccountId == weightMatrix.AccountId, string.Format("The weight matrix found for name '{0}' has a wrong AccountId", weightMatrixName));
        }

        [TestMethod]
        public void SavePeerGroupWeights()
        {
            const string weightMatrixName = "Default Weight Matrix";
            var indicators = new List<string> { "A.1.2", "B.2.1", "C.11.22" };

            var weightMatrix = new WeightMatrix
            {
                Id = Guid.NewGuid(),
                AccountId = null,
                Name = weightMatrixName,
                IndicatorsNumbers = indicators
            };

            var peerGroupWeights = new List<PeerGroupWeights>
            {
                new PeerGroupWeights
                {
                    Id = Guid.NewGuid(),
                    MatrixId = weightMatrix.Id,
                    PeerGroupName = "PeerGroup1",
                    CompanyType = "Public",
                    TemplateType = "Type A",
                    Weights = indicators.Select((i, idx) => new KeyValuePair<string, double>(i, idx)).ToDictionary(i => i.Key, i => i.Value)
                },
                new PeerGroupWeights
                {
                    Id = Guid.NewGuid(),
                    MatrixId = weightMatrix.Id,
                    PeerGroupName = "PeerGroup2",
                    CompanyType = "Public",
                    TemplateType = "Type B",
                    Weights = indicators.Select((i, idx) => new KeyValuePair<string, double>(i, idx)).ToDictionary(i => i.Key, i => i.Value)
                }
            };

            _repository.SavePeerGroupWeights(peerGroupWeights);

            var dbPeerGroupWeights = _repository.ListPeerGroupWeights(new List<Guid> { weightMatrix.Id });

            Assert.IsTrue(dbPeerGroupWeights.ContainsKey(weightMatrix.Id), "Peer group weights were not saved successfully!");
            Assert.IsTrue(dbPeerGroupWeights[weightMatrix.Id].Count == peerGroupWeights.Count, "The number of retrieved peer group weights is wrong");
            Assert.IsTrue(peerGroupWeights.Any(pgw => dbPeerGroupWeights[weightMatrix.Id].Any(dbpgw => pgw.Id == dbpgw.Id)), "The retrieved peer group weights are not the same as the saved ones");
        }

        [TestMethod]
        public void DeletePeerGroupsWeights()
        {
            const string weightMatrixName = "Default Weight Matrix";
            var indicators = new List<string> { "A.1.2", "B.2.1", "C.11.22" };

            var weightMatrix = new WeightMatrix
            {
                Id = Guid.NewGuid(),
                AccountId = null,
                Name = weightMatrixName,
                IndicatorsNumbers = indicators
            };

            var peerGroupWeights = new List<PeerGroupWeights>
            {
                new PeerGroupWeights
                {
                    Id = Guid.NewGuid(),
                    MatrixId = weightMatrix.Id,
                    PeerGroupName = "PeerGroup1",
                    CompanyType = "Public",
                    TemplateType = "Type A",
                    Weights = indicators.Select((i, idx) => new KeyValuePair<string, double>(i, idx)).ToDictionary(i => i.Key, i => i.Value)
                },
                new PeerGroupWeights
                {
                    Id = Guid.NewGuid(),
                    MatrixId = weightMatrix.Id,
                    PeerGroupName = "PeerGroup2",
                    CompanyType = "Public",
                    TemplateType = "Type B",
                    Weights = indicators.Select((i, idx) => new KeyValuePair<string, double>(i, idx)).ToDictionary(i => i.Key, i => i.Value)
                }
            };

            _repository.SavePeerGroupWeights(peerGroupWeights);

            _repository.DeletePeerGroupsWeights(weightMatrix.Id);

            var dbPeerGroupWeights = _repository.ListPeerGroupWeights(new List<Guid> { weightMatrix.Id });

            Assert.IsTrue(dbPeerGroupWeights.Count == 0, "The peer group weights were not removed successfully");
        }

        [TestMethod]
        public void SaveCompanyWeights()
        {
            const string weightMatrixName = "Default Weight Matrix";
            var indicators = new List<string> { "A.1.2", "B.2.1", "C.11.22" };

            var weightMatrix = new WeightMatrix
            {
                Id = Guid.NewGuid(),
                AccountId = null,
                Name = weightMatrixName,
                IndicatorsNumbers = indicators
            };

            var companyWeights = new List<CompanyWeights>
            {
                new CompanyWeights
                {
                    Id = Guid.NewGuid(),
                    MatrixId = weightMatrix.Id,
                    CompanyId = Guid.NewGuid(),
                    Weights = indicators.Select((i, idx) => new KeyValuePair<string, double>(i, idx)).ToDictionary(i => i.Key, i => i.Value)
                },
                new CompanyWeights
                {
                    Id = Guid.NewGuid(),
                    MatrixId = weightMatrix.Id,
                    CompanyId = Guid.NewGuid(),
                    Weights = indicators.Select((i, idx) => new KeyValuePair<string, double>(i, idx)).ToDictionary(i => i.Key, i => i.Value)
                }
            };

            _repository.SaveCompanyWeights(companyWeights);

            var dbCompanyWeights = _repository.ListCompanyWeights(new List<Guid> { weightMatrix.Id });

            Assert.IsTrue(dbCompanyWeights.ContainsKey(weightMatrix.Id), "Peer company were not saved successfully!");
            Assert.IsTrue(dbCompanyWeights[weightMatrix.Id].Count == companyWeights.Count, "The number of retrieved company weights is wrong!");
            Assert.IsTrue(companyWeights.Any(pgw => dbCompanyWeights[weightMatrix.Id].Any(dbcw => pgw.Id == dbcw.Id)), "The retrieved company weights are not the same as the saved ones!");
        }

        [TestMethod]
        public void DeleteCompaniesWeights()
        {
            const string weightMatrixName = "Default Weight Matrix";
            var indicators = new List<string> { "A.1.2", "B.2.1", "C.11.22" };

            var weightMatrix = new WeightMatrix
            {
                Id = Guid.NewGuid(),
                AccountId = null,
                Name = weightMatrixName,
                IndicatorsNumbers = indicators
            };

            var companyWeights = new List<CompanyWeights>
            {
                new CompanyWeights
                {
                    Id = Guid.NewGuid(),
                    MatrixId = weightMatrix.Id,
                    CompanyId = Guid.NewGuid(),
                    Weights = indicators.Select((i, idx) => new KeyValuePair<string, double>(i, idx)).ToDictionary(i => i.Key, i => i.Value)
                },
                new CompanyWeights
                {
                    Id = Guid.NewGuid(),
                    MatrixId = weightMatrix.Id,
                    CompanyId = Guid.NewGuid(),
                    Weights = indicators.Select((i, idx) => new KeyValuePair<string, double>(i, idx)).ToDictionary(i => i.Key, i => i.Value)
                }
            };

            _repository.SaveCompanyWeights(companyWeights);

            _repository.DeleteCompaniesWeights(weightMatrix.Id);

            var dbCompanyWeights = _repository.ListCompanyWeights(new List<Guid> { weightMatrix.Id });

            Assert.IsTrue(dbCompanyWeights.Count == 0, "Company group weights were not removed successfully");
        }

        [TestMethod]
        public void ListWeightMatrices()
        {
            const string weightMatrixName = "Default Weight Matrix";
            var indicators = new List<string> { "A.1.2", "B.2.1", "C.11.22" };

            var weightMatrix1 = new WeightMatrix
            {
                Id = Guid.NewGuid(),
                AccountId = null,
                Name = weightMatrixName,
                IndicatorsNumbers = indicators
            };

            var weightMatrix2 = new WeightMatrix
            {
                Id = Guid.NewGuid(),
                AccountId = weightMatrix1.AccountId,
                Name = "Custom Weight Matrix",
                IndicatorsNumbers = weightMatrix1.IndicatorsNumbers
            };

            _repository.SaveWeightMatrix(weightMatrix1);

            _repository.SaveWeightMatrix(weightMatrix2);

            var dbWeightMatrices = _repository.ListWeightMatrices();

            Assert.IsTrue(dbWeightMatrices.Count == 2, "There were 2 weight matrices expected but a different number was received!");
            Assert.IsTrue(dbWeightMatrices.Any(wm => wm.Id == weightMatrix1.Id || wm.Id == weightMatrix2.Id), "Not all the weight matrices were saved successfully!");
        }

        [TestMethod]
        public void ListCompanyWeightsByCompanyIds()
        {
            //  public  List<CompanyWeights> ListCompanyWeightsByCompanyIds(List<Guid> companyIds)

            var c1 = new CompanyWeights() { CompanyId = Guid.NewGuid(), Id = Guid.NewGuid(), MatrixId = Guid.Empty, Weights = new Dictionary<string, double>() };
            var c2 = new CompanyWeights() { CompanyId = Guid.NewGuid(), Id = Guid.NewGuid(), MatrixId = Guid.Empty, Weights = new Dictionary<string, double>() };
            var c3 = new CompanyWeights() { CompanyId = Guid.NewGuid(), Id = Guid.NewGuid(), MatrixId = Guid.Empty, Weights = new Dictionary<string, double>() };
            var c4 = new CompanyWeights() { CompanyId = Guid.NewGuid(), Id = Guid.NewGuid(), MatrixId = Guid.Empty, Weights = new Dictionary<string, double>() };
            var c5 = new CompanyWeights() { CompanyId = Guid.NewGuid(), Id = Guid.NewGuid(), MatrixId = Guid.Empty, Weights = new Dictionary<string, double>() };
            var c6 = new CompanyWeights() { CompanyId = Guid.NewGuid(), Id = Guid.NewGuid(), MatrixId = Guid.Empty, Weights = new Dictionary<string, double>() };
            var c7 = new CompanyWeights() { CompanyId = Guid.NewGuid(), Id = Guid.NewGuid(), MatrixId = Guid.Empty, Weights = new Dictionary<string, double>() };

            var input = new List<CompanyWeights>();
            input.Add(c1);
            input.Add(c2);
            input.Add(c3);
            input.Add(c4);
            input.Add(c5);
            input.Add(c6);
            input.Add(c7);

            _repository.SaveCompanyWeights(input);

            var toSearchFor = input.Select(x => x.CompanyId).ToList();
            toSearchFor[0] = Guid.NewGuid();

            var result = _repository.ListCompanyWeightsByCompanyIds(toSearchFor);
            Assert.IsTrue(result.Count == input.Count - 1, " search for company id is failed ");
        }

        [TestMethod]
        public void RemoveCompanyWeightsByCompanyIds()
        {
            var c1 = new CompanyWeights() { CompanyId = Guid.NewGuid(), Id = Guid.NewGuid(), MatrixId = Guid.Empty, Weights = new Dictionary<string, double>() };
            var c2 = new CompanyWeights() { CompanyId = Guid.NewGuid(), Id = Guid.NewGuid(), MatrixId = Guid.Empty, Weights = new Dictionary<string, double>() };
            var c3 = new CompanyWeights() { CompanyId = Guid.NewGuid(), Id = Guid.NewGuid(), MatrixId = Guid.Empty, Weights = new Dictionary<string, double>() };
            var c4 = new CompanyWeights() { CompanyId = Guid.NewGuid(), Id = Guid.NewGuid(), MatrixId = Guid.Empty, Weights = new Dictionary<string, double>() };
            var c5 = new CompanyWeights() { CompanyId = Guid.NewGuid(), Id = Guid.NewGuid(), MatrixId = Guid.Empty, Weights = new Dictionary<string, double>() };
            var c6 = new CompanyWeights() { CompanyId = Guid.NewGuid(), Id = Guid.NewGuid(), MatrixId = Guid.Empty, Weights = new Dictionary<string, double>() };
            var c7 = new CompanyWeights() { CompanyId = Guid.NewGuid(), Id = Guid.NewGuid(), MatrixId = Guid.Empty, Weights = new Dictionary<string, double>() };

            var input = new List<CompanyWeights>();
            input.Add(c1);
            input.Add(c2);
            input.Add(c3);
            input.Add(c4);
            input.Add(c5);
            input.Add(c6);
            input.Add(c7);

            _repository.SaveCompanyWeights(input);

            var toSearchFor = input.Select(x => x.CompanyId).ToList();
            toSearchFor[0] = Guid.NewGuid();

            var result = _repository.ListCompanyWeightsByCompanyIds(toSearchFor);
            Assert.IsTrue(result.Count == input.Count - 1, " search for company id is failed ");

            _repository.RemoveCompanyWeightsByCompanyIds(input.Select(x => x.CompanyId));

            result = _repository.ListCompanyWeightsByCompanyIds(toSearchFor);
            Assert.IsTrue(result.Count == 0, " remove companyid failed ");
        }
    }
}
